#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

using namespace std;

const int gmaxcap = 500;

template <class T>
class BlockQueue
{
    public:
        BlockQueue(const int &maxcap = gmaxcap):_maxcap(maxcap)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_pcond, nullptr);
            pthread_cond_init(&_ccond, nullptr);
        }

        void push(const T &in)// 输入型参数 -> const &
        {
            pthread_mutex_lock(&_mutex);
            // 判断
            // 细节2: 充当条件判断的语法必须是while,不能是if
            while(is_full())
            {
                // 细节1: pthread_cond_wait这个函数的第二个参数,必须是我们正在使用的互斥锁
                // a. pthread_cond_wait: 该函数调用的时候,会以原子性的方式,将锁释放,并将自己挂起
                // b. pthread_cond_wait: 该函数在被唤醒的时候,会自动的重新获取你传入的锁
                pthread_cond_wait(&_pcond, &_mutex);// 满了,无法生产,生产者进行等待
            }

            // 这里一定是没有满的,可以生产
            _q.push(in);

            // 能够保证阻塞队列一定有数据
            // 细节3: pthread_cond_signal: 这个函数可以放在临界区内部,也可以放在临界区外部
            pthread_cond_signal(&_ccond);// 有了数据,可以唤醒消费者
            pthread_mutex_unlock(&_mutex);
        }

        void pop(T *out)// 输出型参数 -> *   输入输出型 -> &
        {
            pthread_mutex_lock(&_mutex);
            // 判断
            while(is_empty())
            {
                pthread_cond_wait(&_ccond, &_mutex);// 空的,无法消费,进入等待
            }

            // 能够保证不为空
            *out = _q.front();
            _q.pop();

            // 保证有一个空的位置
            pthread_cond_signal(&_pcond);// 有一个空的,可以唤醒生产者
            pthread_mutex_unlock(&_mutex);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_pcond);
            pthread_cond_destroy(&_ccond);
        }
    private:
        bool is_empty()
        {
            return _q.empty();
        }

        bool is_full()
        {
            return _q.size() == _maxcap;
        }
    private:
        queue<T> _q;// 阻塞队列
        int _maxcap;// 队列中元素的上限
        pthread_mutex_t _mutex;// 互斥锁保护队列安全
        pthread_cond_t _pcond;// 生产者对应的环境变量
        pthread_cond_t _ccond;// 消费者对应的环境变量
};